﻿#include "_video.h"
#include "demux.h"
#include "audio_stream.h"
#include "video_stream.h"

static void* _play_routine(void* p);
static void* _record_routine_v(void* p);
static void* _record_routine_a(void* p);

VIDEO_CONTEXT* g_video_context = 0;

VIDEO_ERROR _video_init()
{
	VIDEO_ERROR res = 0;
	g_video_context = (VIDEO_CONTEXT*)MALLOC(sizeof(VIDEO_CONTEXT));
	if (g_video_context == 0)
	{
		return VIDEO_ERROR_02;
	}

	memset(g_video_context, 0, sizeof(VIDEO_CONTEXT));

	if (res == 0)res = mem_init();
	if (res == 0)res = demux_init();
	if (res == 0)res = audio_stream_init();
	if (res == 0)res = video_stream_init();
	if (res)_video_free();

	return res;
}

VIDEO_ERROR _video_free()
{
	assert(g_video_context->favi.handle == (void*)0);
	assert(g_video_context->queue1 == (void*)0);
	assert(g_video_context->queue2 == (void*)0);
	assert(g_video_context->queue3 == (void*)0);
	assert(g_video_context->thread1 == (void*)0);
	assert(g_video_context->thread2 == (void*)0);

	demux_free();
	audio_stream_free();
	video_stream_free();
	mem_free();

	if (g_video_context)
	{
		FREE(g_video_context);
		g_video_context = 0;
	}

	return VIDEO_ERROR_OK;
}


VIDEO_ERROR _play_start(char* fname, VIDEO_CB cb)
{
#ifndef MAKELONG
#define MAKELONG(a, b)      ((long)(((unsigned short)(((unsigned long)(a)) & 0xffff)) | ((unsigned long)((unsigned short)(((unsigned long)(b)) & 0xffff))) << 16))
#endif

	VIDEO_ERROR res = 0;
	OAL_QUEUE_POOL pool = { sizeof(VIDEO_MSG) * 2 };

	strcpy((char*)g_video_context->fname, fname);
	g_video_context->cb = cb;

	if (res == 0)res = f_open(&g_video_context->favi, (char*)fname, FA_READ);
	if (res == 0)res = demux_file_head();
	if (res == 0)res = audio_driver_open(1);
	if (res == 0)res = video_driver_open(1);
	if (res == 0)res = OAL_queue_create(&g_video_context->queue1, (char*)"queue1", sizeof(VIDEO_MSG), &pool);
	if (res == 0)g_video_context->thread1 = OAL_thread_create(_play_routine, 0, "play", OAL_THREAD_PRIORITY_NORMAL, 2048);
	if (res)_play_stop();

	if (g_video_context->cb)g_video_context->cb(VIDEO_HEAD, g_avi_context->TotalFrame);
	if (g_video_context->cb)g_video_context->cb(VIDEO_VIDEO_SIZE, MAKELONG(g_avi_context->Width, g_avi_context->Height));
	return res;
}

VIDEO_ERROR _play_stop()
{
	VIDEO_MSG msg = { MSGID_QUIT, 0, 0 };
	if (g_video_context->queue1)OAL_queue_send(&g_video_context->queue1, &msg, OAL_WAIT_FOREVER);
	if (g_video_context->thread1)OAL_thread_join(g_video_context->thread1);
	if (g_video_context->queue1)OAL_queue_delete(&g_video_context->queue1);

	g_video_context->queue1 = (void*)0;
	g_video_context->thread1 = (void*)0;

#ifdef WIN32
	if(g_video_context->favi.handle)f_close(&g_video_context->favi);
	g_video_context->favi.handle = 0;
#else
	f_close(&g_video_context->favi);
#endif

	audio_driver_close(1);
	video_driver_close(1);

	return VIDEO_ERROR_OK;
}

VIDEO_ERROR _play_pause()
{
	VIDEO_ERROR res = 0;
	VIDEO_MSG msg = { MSGID_PAUSE, 1, 0 };
	OAL_queue_send(&g_video_context->queue1, &msg, OAL_WAIT_FOREVER);
	return res;
}

VIDEO_ERROR _play_resume()
{
	VIDEO_ERROR res = 0;
	VIDEO_MSG msg = { MSGID_PAUSE, 0, 0 };
	OAL_queue_send(&g_video_context->queue1, &msg, OAL_WAIT_FOREVER);
	return res;
}

VIDEO_ERROR _play_seek(int offset)
{
	VIDEO_ERROR res = 0;
	VIDEO_MSG msg = { MSGID_SEEK, offset, 0 };
	OAL_queue_send(&g_video_context->queue1, &msg, OAL_WAIT_FOREVER);
	return res;
}

VIDEO_ERROR _play_set_volume(unsigned char volume)
{
	VIDEO_ERROR res = 0;
	VIDEO_MSG msg = { MSGID_VOLUME, volume, 0 };
	OAL_queue_send(&g_video_context->queue1, &msg, OAL_WAIT_FOREVER);
	return res;
}


VIDEO_ERROR _record_start(char* fname, VIDEO_CB cb)
{
	VIDEO_ERROR res = 0;
	OAL_QUEUE_POOL pool = { sizeof(VIDEO_MSG) * 4 };

	strcpy((char*)g_video_context->fname, fname);
	g_video_context->cb = cb;

	if (res == 0)res = f_open(&g_video_context->favi, (char*)fname, FA_READ | FA_WRITE | FA_CREATE_ALWAYS);
	if (res == 0)res = mux_file_head();
	if (res == 0)res = OAL_queue_create(&g_video_context->queue1, (char*)"queue1", sizeof(VIDEO_MSG), &pool);
	if (res == 0)res = OAL_queue_create(&g_video_context->queue2, (char*)"queue2", sizeof(VIDEO_MSG), &pool);
	if (res == 0)res = OAL_queue_create(&g_video_context->queue3, (char*)"queue3", sizeof(VIDEO_MSG), &pool);
	if (res == 0)g_video_context->thread1 = OAL_thread_create(_record_routine_v, 0, "record_v", OAL_THREAD_PRIORITY_NORMAL, 512);
	if (res == 0)g_video_context->thread2 = OAL_thread_create(_record_routine_a, 0, "record_a", OAL_THREAD_PRIORITY_NORMAL, 512);
	if (res)_record_stop();

	return res;
}

VIDEO_ERROR _record_stop()
{
	VIDEO_MSG msg = { MSGID_QUIT, 0, 0 };

	if (g_video_context->queue1)OAL_queue_send(&g_video_context->queue1, &msg, OAL_WAIT_FOREVER);
	if (g_video_context->queue2)OAL_queue_send(&g_video_context->queue2, &msg, OAL_WAIT_FOREVER);
	if (g_video_context->thread1)OAL_thread_join(g_video_context->thread1);
	if (g_video_context->thread2)OAL_thread_join(g_video_context->thread2);

	if (g_video_context->queue1)OAL_queue_delete(&g_video_context->queue1);
	if (g_video_context->queue2)OAL_queue_delete(&g_video_context->queue2);
	if (g_video_context->queue3)OAL_queue_delete(&g_video_context->queue3);

	g_video_context->queue1 = (void*)0;
	g_video_context->queue2 = (void*)0;
	g_video_context->queue3 = (void*)0;
	g_video_context->thread1 = (void*)0;
	g_video_context->thread2 = (void*)0;
	
#ifdef WIN32
	if (g_video_context->favi.handle)f_close(&g_video_context->favi);
	g_video_context->favi.handle = 0;
#else
	f_close(&g_video_context->favi);
	OAL_sleep(1000);
#endif

	return VIDEO_ERROR_OK;
}

VIDEO_ERROR _record_pause()
{
	VIDEO_ERROR res = 0;
	VIDEO_MSG msg = { MSGID_PAUSE, 1, 0 };
	OAL_queue_send(&g_video_context->queue1, &msg, OAL_WAIT_FOREVER);
	return res;
}

VIDEO_ERROR _record_resume()
{
	VIDEO_ERROR res = 0;
	VIDEO_MSG msg = { MSGID_PAUSE, 0, 0 };
	OAL_queue_send(&g_video_context->queue1, &msg, OAL_WAIT_FOREVER);
	return res;
}

static void* _play_routine(void* p)
{
	p = p;
	VIDEO_ERROR res;
	VIDEO_MSG msg;

	video_driver_start(1);

	for (;;)
	{
		if (OAL_SUCCESS == OAL_queue_recv(&g_video_context->queue1, &msg, 0))
		{
			switch (msg.id)
			{
			case MSGID_QUIT:
				//quit
				return (void*)0;
			case MSGID_SEEK:
				//AVIPlayer_seek
				//msg.wp;
				break;
			case MSGID_PAUSE:
				//AVIPlayer_pause or AVIPlayer_resume
				g_video_context->pause = msg.wp;
				break;
			case MSGID_VOLUME:
				//AVIPlayer_set_volume
				g_video_context->volume = (uint8_t)msg.wp;
				break;
			default:
				//not arrived
				assert(0);
				break;
			}
		}
		else
		{
			if (g_video_context->pause)
			{
				OAL_sleep(100);
			}
			else
			{
				res = demux_next_block();
				switch (res)
				{
				case VIDEO_ERROR_OK:
					break;
				case VIDEO_ERROR_99:
					if (g_video_context->cb)g_video_context->cb(VIDEO_OVER, 0);
					return (void*)0;
				default:
					if (g_video_context->cb)g_video_context->cb(VIDEO_STREAM_ERR, res);
					return (void*)0;
				}
			}
		}
	}
}

static VIDEO_ERROR on_video_frm_start()
{
	g_avi_context->frame_v.h.fccid.id = g_avi_context->VideoFLAG;
	g_avi_context->frame_v.h.size = 0;
	g_avi_context->frame_v.offset = f_tell(&g_video_context->favi);
	return mux_next_block_v(&g_avi_context->frame_v);
}

static VIDEO_ERROR on_video_frm_end()
{
	VIDEO_ERROR res = 0;

	g_avi_context->avi_file_map.avi_header.mainaviheader.dwTotalFrames++;
	g_avi_context->avi_file_map.vids_strl.vids_header.dwLength++;

	FSIZE_t ofs = f_tell(&g_video_context->favi);
	
	res = f_lseek(&g_video_context->favi, g_avi_context->frame_v.offset);
	if (res)return VIDEO_ERROR_16;

	res = mux_next_block_v(&g_avi_context->frame_v);
	if (res)return res;

	f_lseek(&g_video_context->favi, ofs);

	res = mux_ensure_alignment();
	if (res)return res;
	return VIDEO_ERROR_OK;
}

static VIDEO_ERROR substate_audio()
{
	VIDEO_ERROR res = 0;
	VIDEO_MSG msg;
	FRAME frame = {0};

	for(;;)
	{
		if (OAL_SUCCESS == OAL_queue_recv(&g_video_context->queue3, &msg, 0))
		{
			switch (msg.id)
			{
			case MSGID_AUDIO_FRM:
				frame.h.fccid.id = g_avi_context->AudioFLAG;
				frame.h.size = (u32)msg.lp;
				frame.buff = (void*)msg.wp;
				frame.offset = (FSIZE_t)(-1);
				res = mux_next_block_a(&frame);
				if (res)return VIDEO_ERROR_17;
				break;

			default:
				//not arrived
				assert(0);
				break;
			}
		}
		else
		{
			return VIDEO_ERROR_OK;
		}
	}
}

static void* _record_routine_v(void* p)
{
	p = p;
	VIDEO_ERROR res = 0;
	VIDEO_MSG msg;
	
	res = video_driver_open(0);
	if (res)return (void*)-2;

	video_driver_start(0);
	on_video_frm_start();
	
	for (;;)
	{
		if (OAL_SUCCESS == OAL_queue_recv(&g_video_context->queue1, &msg, OAL_WAIT_FOREVER))
		{
			switch (msg.id)
			{
			case MSGID_QUIT:
				res = substate_audio();
				video_driver_close(0);
				res = mux_file_end();
				return (void*)0;

			case MSGID_H264_PKG:
				res = video_driver_pkg();
				if (res)
				{
					if (g_video_context->cb)g_video_context->cb(VIDEO_FRAME_V, 0);
					return (void*)-2;
				}
				break;

			case MSGID_H264_FRM:
				res = video_driver_frm();
				res += on_video_frm_end();
				if (res)
				{
					if (g_video_context->cb)g_video_context->cb(VIDEO_FRAME_V, 0);
					return (void*)-2;
				}
				res = substate_audio();
				if (res)
				{
					if (g_video_context->cb)g_video_context->cb(VIDEO_FRAME_A, 0);
					return (void*)-2;
				}
				on_video_frm_start();
				break;

			default:
				//not arrived
				assert(0);
				break;
			}
		}
	}
}

static VIDEO_ERROR _record_on_audio()
{
	VIDEO_ERROR res = 0;

	if (g_avi_context->frame_a.buff == 0)
	{
		g_avi_context->frame_a.h.fccid.id = g_avi_context->AudioFLAG;
		g_avi_context->frame_a.h.size = 0;
		g_avi_context->frame_a.buff = mem_get_buff();
	}

	u8* paudio = g_avi_context->frame_a.buff;
	u32 len = PCM_CHUNK;
	res = audio_driver_read(&paudio[g_avi_context->frame_a.h.size], &len);
	if (res)return res;
	g_avi_context->frame_a.h.size += len * 2;

	if (g_avi_context->frame_a.h.size >= AUDIO_FRM_SIZE)
	{
		res = audio_stream_frame_in(&g_avi_context->frame_a);
		if (res)return res;

		VIDEO_MSG msg = { MSGID_AUDIO_FRM, (int)g_avi_context->frame_a.buff, (int)g_avi_context->frame_a.h.size };
		if (g_video_context->queue1)OAL_queue_send(&g_video_context->queue3, &msg, OAL_WAIT_FOREVER);
		g_avi_context->frame_a.h.size = 0;
		g_avi_context->frame_a.buff = 0;
		g_avi_context->avi_file_map.auds_strl.auds_header.dwLength++;
	}

	return res;
}

static void* _record_routine_a(void* p)
{
	p = p;
	VIDEO_ERROR res = 0;
	VIDEO_MSG msg;

	res = audio_driver_open(0);
	if (res)return (void*)-2;

	for(;;)
	{
		if (OAL_SUCCESS == OAL_queue_recv(&g_video_context->queue2, &msg, 0))
		{
			switch (msg.id)
			{
			case MSGID_QUIT:
				audio_driver_close(0);
				return (void*)0;

			default:
				//not arrived
				assert(0);
				break;
			}
		}
		else
		{
			res = _record_on_audio();
			if (res)
			{
				if (g_video_context->cb)g_video_context->cb(VIDEO_FRAME_A, 0);
				return (void*)-2;
			}
		}
	}

}
